/**
 * Copyright (c) 2012, Hakans Pirates - 
 * Johan Andersson spaiki17@gmail.com, 
 * Mattias Lundberg lundberg.mattias@gmail.com, 
 * Samuel Sjodin sjoodin@gmail.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *  
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package pirates;

import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.math.Vector3f;
import com.jme3.network.Network;
import com.jme3.network.Server;
import com.jme3.network.kernel.ConnectorException;
import com.jme3.network.kernel.KernelException;
import java.util.concurrent.LinkedBlockingQueue;

import pirates.Settings.EntityModels;
import pirates.Settings.EntityTeam;
import pirates.Settings.ShipConstants;
import pirates.crates.FloatingCrate;
import pirates.inputDevices.AutomaticControl;
import pirates.network.ServerNetListener;
import pirates.network.ServerNetSender;
import pirates.network.messages.EndGameMessage;
import pirates.notifications.Notify;
import pirates.ship.ShipControl;
import pirates.ship.ShipState;
import pirates.terrain.TerrainState;

/**
 * 
 * @author Samuel
 */
public class PiratesServer extends SimpleApplication {

	ServerNetListener netListener;
	private Server myServer;
	public ShipState ships;
	public BulletAppState bulletAppState;
	private Thread serverNetSenderThread;
	private ServerNetSender netSender;
	private Node crateNode;
	private CrateList crates = new CrateList();

	@Override
	public void simpleInitApp() {

		bulletAppState = new BulletAppState();
		stateManager.attach(bulletAppState);

		TerrainState terrain = new TerrainState(this);
		ships = new ShipState(this, myServer);

		assetManager.registerLocator("assets/", FileLocator.class); // needed so
																	// that
																	// running
																	// in
																	// Eclipse
																	// works
		terrain.setEnabled(true);
		ships.setEnabled(true);

		stateManager.attach(terrain);
		stateManager.attach(ships);

		try {
			setServer(Network.createServer(Settings.NW_PORT));
			netListener = new ServerNetListener(getServer(), this);
			getServer().start();
			netSender = new ServerNetSender(myServer, this);
			serverNetSenderThread = new Thread(netSender);
			serverNetSenderThread.start();

		} catch (IOException e) {
			Notify.except(e);
			System.exit(0);
		} catch (ConnectorException e) {
			Notify.stdout("At server: Connection unexpectedly closed.");
			System.exit(0);
		} catch (KernelException e) {
			Notify.stdout("Cannot start server. Has a server process already been started?");
			System.exit(0);
		}

		crateNode = new Node("Crates");
		rootNode.attachChild(crateNode);

		crates.init(this, getServer());
		crates.generateCrates(Settings.START_CRATES);

		setUpAutomaticShips();
		Settings.SENDING_NW_UPDATES = true;
		// KeyMouseState keyMouse = new KeyMouseState(this, new
		// ChatDisplay(guiNode, assetManager)
		// ,ships.getShip("Johan").getControl(VehicleControl.class));
		// stateManager.attach(keyMouse);
		Notify.stdout("Server successfully started.");
	}

	public Server getServer() {
		return myServer;
	}

	public void setServer(Server myServer) {
		this.myServer = myServer;
	}

	public Node getCrateNode() {
		return crateNode;
	}

	/**
	 * call this method to set up the tradeships
	 */
	void setUpAutomaticShips() {
		createTradeship("TheEasternTrader",
				Settings.BrittishSpawnPoint.add(new Vector3f(700, 0, 200)));
		createTradeship("TheJuvelOfBritain",
				Settings.BrittishSpawnPoint.add(new Vector3f(500, 0, 500)));
	}

	/**
	 * call this after everything has been set up, and enough players has
	 * arrived
	 */
	public void startScenario() {
		System.out
				.println("Enough players has joined the game on both sides. Scenario started!");
		Iterator<Spatial> aiShipsIterator = ships.getAIships();
		while (aiShipsIterator.hasNext()) {
			Spatial aiShip = aiShipsIterator.next();
			Notify.debug(aiShip.getName() + " is departing!");
			AutomaticControl aiControl = aiShip
					.getControl(AutomaticControl.class);
			aiControl.go(); // make tradeships move.
		}
		Settings.SCENARIO_HAS_STARTED = true;
	}

	public void stopScenarioAndDeclareWinner(EntityTeam winningTeam) {

		// Iterator<Spatial> aiShipsIterator = ships.getAIships();
		// while(aiShipsIterator.hasNext()){
		// Spatial aiShip = aiShipsIterator.next();
		// AutomaticControl aiControl =
		// aiShip.getControl(AutomaticControl.class);
		// aiControl.stop(); //make tradeships stop.
		// }
		if (Settings.SCENARIO_HAS_ENDED) {
			Notify.debug("stop scenario was called too many times - ignore call");
		} else {
			Notify.debug("Stopping scenario");
			Settings.SCENARIO_HAS_ENDED = true;
			Settings.winningTeam = winningTeam;
			myServer.broadcast(new EndGameMessage(winningTeam.toString()));
		}

	}

	private void createTradeship(String name, Vector3f pos) {
		// ships.createShip(
		// EntityModels.FLAGSHIP,
		// Settings.BrittishSpawnPoint,
		// name,
		// EntityModels.FLAGSHIP.getValues(),
		// EntityTeam.NEUTRAL.getValues());
		Map<String, Object> mapping;
		mapping = EntityModels.FLAGSHIP.getValues();// model-specific values
		mapping.putAll(EntityTeam.NEUTRAL.getValues()); // team-specific values
		mapping.put(ShipConstants.OWNER.toString(), Settings.HOST_NAME); // marking
																			// the
																			// ship
																			// to
																			// tell
																			// that
																			// it
																			// is
																			// owned
																			// by
																			// this
																			// server
		ships.createShip(
				EntityModels.FLAGSHIP.getValues()
						.get(ShipConstants.MODEL_PATH.toString()).toString(),// shipmodel
																				// //Settings.BATTLESHIP_MODEL,
				pos, // location
				name, // name/id
				mapping);
		Spatial ship = ships.getShip(name);
		ship.addControl(new AutomaticControl(
				ship.getControl(ShipControl.class), this, ship));

	}

	public CrateList getCrates() {
		return crates;
	}
}
